package fem2;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import inf.text.ArrayFormat;

import math2.ConstantFunctionRnToR;
import math2.FunctionRnToR;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import fem2.analysis.Analysis;
import fem2.analysis.LinearStaticAnalysis;
import fem2.element.BBarStructuralElement;
import fem2.element.StructuralElement;
import fem2.enu.State;
import fem2.material.StVenantKirchhoffMM;

@RunWith(Parameterized.class)
public class PatchTestTet4 {

	@Parameters
	public static Collection<Object[]> parameters() {
		List<Object[]> params = new LinkedList<Object[]>();

		params.add(new Object[] { 0 });
		params.add(new Object[] { 1 });
		return params;
	}

	private int option;

	public PatchTestTet4(int option) {
		this.option = option;
	}

	public Mesh createMesh() {
		Mesh mesh = new Mesh();

		Node n1 = mesh.addNode(new Node(0, 1, 1));
		Node n2 = mesh.addNode(new Node(0, 1, 0));
		Node n3 = mesh.addNode(new Node(0, 0, 1));
		Node n4 = mesh.addNode(new Node(1, 1, 1));//
		Node n5 = mesh.addNode(new Node(0, 0, 0));
		Node n6 = mesh.addNode(new Node(1, 1, 0));//
		Node n7 = mesh.addNode(new Node(1, 0, 1));//
		Node n8 = mesh.addNode(new Node(1, 0, 0));//

		mesh.addBlock(new Block(n5, n3, n7, n4));
		mesh.addBlock(new Block(n5, n8, n6, n4));
		mesh.addBlock(new Block(n2, n1, n5, n6));
		mesh.addBlock(new Block(n7, n8, n5, n4));
		mesh.addBlock(new Block(n3, n1, n4, n5));
		mesh.addBlock(new Block(n6, n4, n1, n5));

		mesh.setNumberOfFields(3);

		Constraint cxyz = new Constraint(false, false, false);//
		Constraint cxy = new Constraint(false, false, true);//
		Constraint cyz = new Constraint(true, false, false);//
		Constraint cxz = new Constraint(false, true, false);//
		Constraint cx = new Constraint(false, true, true);//
		Constraint cy = new Constraint(true, false, true);
		Constraint cz = new Constraint(true, true, false);//
		MeshUtilities.seekNode(mesh, 0, 0, 0).setConstraint(cxyz);
		MeshUtilities.seekNode(mesh, 1, 0, 0).setConstraint(cyz);
		MeshUtilities.seekNode(mesh, 0, 1, 0).setConstraint(cxz);
		MeshUtilities.seekNode(mesh, 0, 0, 1).setConstraint(cxy);
		MeshUtilities.seekNode(mesh, 1, 1, 0).setConstraint(cz);
		MeshUtilities.seekNode(mesh, 1, 0, 1).setConstraint(cy);
		MeshUtilities.seekNode(mesh, 0, 1, 1).setConstraint(cx);

		return mesh;
	}

	public Model createModel() {
		Mesh mesh = createMesh();
		Model M = new Model(mesh);

		double E = 2.0;
		double nu = 0.3;
		double t = 1.0;
		State ss = State.THREE_DIMENSIONAL;
		MaterialModel mm = new StVenantKirchhoffMM(E, nu, t, 0.0, ss);

		for (int i = 0; i < mesh.countBlocks(); i++) {
			Element e;
			switch (option) {
			case 0:
				e = new StructuralElement(mesh.getBlock(i), mm);
				break;
			case 1:
				e = new BBarStructuralElement(mesh.getBlock(i), mm);
				break;
			default:
				throw new Error("invalid option");
			}
			M.addElement(e);
		}

		return M;
	}

	@Test
	public void testSolve1() {
		Model M = createModel();
		Mesh mesh = M.getMesh();
		double p = 2.0;
		Force f1 = new Force(MeshUtilities.seekNode(mesh, 1, 0, 0), p / 3, 0, 0);
		Force f2 = new Force(MeshUtilities.seekNode(mesh, 1, 1, 0), p / 6, 0, 0);
		Force f3 = new Force(MeshUtilities.seekNode(mesh, 1, 0, 1), p / 6, 0, 0);
		Force f4 = new Force(MeshUtilities.seekNode(mesh, 1, 1, 1), p / 3, 0, 0);
		M.addLoad(f1);
		M.addLoad(f2);
		M.addLoad(f3);
		M.addLoad(f4);

		Analysis an = new LinearStaticAnalysis(M);
		an.run();

		System.out.println("testSolve1 results:");
		for (int i = 0; i < M.getMesh().countNodes(); i++) {
			double[] uHatLocal = M.getMesh().getNode(i).getUHatLocal();
			System.out.println(ArrayFormat.format(uHatLocal));
		}
		double[][] X = new double[][] { { 0, 0, 0 }, { 1, 0, 0 }, { 1, 1, 0 }, { 0, 1, 0 },
				{ 0, 0, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 0, 1, 1 } };
		double[][] Uh = new double[][] { { 0, 0, 0 }, { 1, 0, 0 }, { 1, -0.3, 0 }, { 0, -0.3, 0 },
				{ 0, 0, -0.3 }, { 1, 0, -0.3 }, { 1, -0.3, -0.3 }, { 0, -0.3, -0.3 } };
		for (int i = 0; i < 8; i++) {
			Node n = MeshUtilities.seekNode(mesh, X[i]);
			double[] uHatLocal = n.getUHatLocal();
			Assert.assertEquals(Uh[i][0], uHatLocal[0], 1e-10);
			Assert.assertEquals(Uh[i][1], uHatLocal[1], 1e-10);
			Assert.assertEquals(Uh[i][2], uHatLocal[2], 1e-10);
		}
	}

	@Test
	public void testSolve2() {
		Model M = createModel();
		Mesh mesh = M.getMesh();
		double p = 2.0;
		FunctionRnToR pf = new ConstantFunctionRnToR(p);

		Node n2 = MeshUtilities.seekNode(mesh, 1, 0, 0);
		Node n3 = MeshUtilities.seekNode(mesh, 1, 1, 0);
		Node n6 = MeshUtilities.seekNode(mesh, 1, 0, 1);
		Node n7 = MeshUtilities.seekNode(mesh, 1, 1, 1);
		Face f1 = mesh.addFace(new Face(n2, n3, n7));
		Face f2 = mesh.addFace(new Face(n2, n6, n7));
		mesh.setNumberOfFields(3);

		DistributedLoad l1 = new DistributedLoad(f1, pf, null, null);
		DistributedLoad l2 = new DistributedLoad(f2, pf, null, null);
		M.addLoad(l1);
		M.addLoad(l2);

		double[] re1 = l1.computeRe(0.0);
		double[] re2 = l2.computeRe(0.0);
		System.out.println(ArrayFormat.format(re1));
		System.out.println(ArrayFormat.format(re2));

		Analysis an = new LinearStaticAnalysis(M);
		an.run();

		System.out.println("testSolve1 results:");
		for (int i = 0; i < M.getMesh().countNodes(); i++) {
			double[] uHatLocal = M.getMesh().getNode(i).getUHatLocal();
			System.out.println(ArrayFormat.format(uHatLocal));
		}
		double[][] X = new double[][] { { 0, 0, 0 }, { 1, 0, 0 }, { 1, 1, 0 }, { 0, 1, 0 },
				{ 0, 0, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 0, 1, 1 } };
		double[][] Uh = new double[][] { { 0, 0, 0 }, { 1, 0, 0 }, { 1, -0.3, 0 }, { 0, -0.3, 0 },
				{ 0, 0, -0.3 }, { 1, 0, -0.3 }, { 1, -0.3, -0.3 }, { 0, -0.3, -0.3 } };
		for (int i = 0; i < 8; i++) {
			Node n = MeshUtilities.seekNode(mesh, X[i]);
			double[] uHatLocal = n.getUHatLocal();
			Assert.assertEquals(Uh[i][0], uHatLocal[0], 1e-10);
			Assert.assertEquals(Uh[i][1], uHatLocal[1], 1e-10);
			Assert.assertEquals(Uh[i][2], uHatLocal[2], 1e-10);
		}
	}
}
